/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algorithm;

import datastructure.BinarySearchTree;
import datastructure.BinarySearchTree.Node;
import datastructure.Fraction;
import datastructure.Pair;
import java.io.*;
import java.util.ArrayList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import utility.FileReader;
import utility.InstancesConverter;
import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayes;
import weka.classifiers.trees.Id3;
import weka.core.Instance;
import weka.core.Instances;
import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.trees.Id3;
import weka.core.Instance;
import weka.core.Instances;
/**
 *
 * @author Winzelric
 */
public class DecisionTreeModel {
    BinarySearchTree tree;
    Id3 model;

    public DecisionTreeModel() {
        tree = new BinarySearchTree();
    }
    
    public DecisionTreeModel(Id3 model){
        this.model = model;
    }
    
    public DecisionTreeModel(BinarySearchTree.Node node) {
        tree = new BinarySearchTree();
        tree.root = node;
    }
    
    public String executeInstance(Instance instance){
        ArrayList<String> input = InstancesConverter.convert2(instance);
        Node node = tree.root;
        
        String temp = tree.root.key;                
        int idx = StringToInt(temp.substring(1));
        
        while(!temp.equals("0") && !temp.equals("1")){
            if(input.get(idx).equals("0")){                
                node = node.left;                
            } else {
                node = node.right;                
            }                           
            
            temp = node.key;
            idx = StringToInt(temp.substring(1));
        }       

        return temp;
    }
    
    public String executeInstance(ArrayList<String> input){        
        Node node = tree.root;
        
        String temp = tree.root.key;                
        int idx = StringToInt(temp.substring(1));
        
        while(!temp.equals("0") && !temp.equals("1")){
            if(input.get(idx).equals("0")){                
                node = node.left;                
            } else {
                node = node.right;                
            }                           
            
            temp = node.key;
            idx = StringToInt(temp.substring(1));
        }       

        return temp;
    }
    
    public String executeInstanceWEKA(Instance instance) {
        Id3 id = new Id3();
        try {
            return Double.toString(model.classifyInstance(instance));
        } catch (Exception e) {
            System.out.println(e);
            return "";
        }        
    }
    
    public void saveModel(String path) throws IOException {
        Object test = new Object();
        test = tree;
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(path));
        oos.writeObject(test);
        oos.flush();
        oos.close();
    }

    public void loadModel(String path) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(path));
        Object test = (Object) ois.readObject();
        tree = (BinarySearchTree) test;        
        ois.close();
    }
    
  public void saveModelWEKA(String path) throws Exception {
    weka.core.SerializationHelper.write(path, model);
  }

  public void loadModelWEKA(String path) throws Exception {
    model = (Id3) weka.core.SerializationHelper.read(path);
  }    
    
    public boolean isValue(BinarySearchTree.Node node){
        if(node.key.length()>1){
            return false;
        }
        return true;
    }
        
    public String getValue(BinarySearchTree.Node node, ArrayList<String> input){
        String temp = null;
        if(isValue(node)){
            temp = node.key;
        } else {
            int attNo = charToInt(node.key.charAt(1));
            if(input.get(attNo-1).equals("0")){
                temp = getValue(node.left, input);
            } else {
                temp = getValue(node.right, input);
            }
        }
        return temp;        
    }
    
    public int charToInt(char c){
        if(c=='0'){return 0;}
        else if(c=='1'){return 1;}
        else if(c=='2'){return 2;}
        else if(c=='3'){return 3;}
        else if(c=='4'){return 4;}
        else if(c=='5'){return 5;}
        else if(c=='6'){return 6;}
        else if(c=='7'){return 7;}
        else if(c=='8'){return 8;}
        else if(c=='9'){return 9;}
        else {return 0;}
    }

/*
        public static void main(String[] asdf){

        System.out.println("masuk main~");
        String a = "C:\\AI\\coba.arff";
        FileReader file = new FileReader();

        Instances qwe = file.readFile(a);
        DecisionTree asd = new DecisionTree();
        DecisionTreeModel zxc = asd.generateModel(qwe);

        zxc.crossv(10);



        }
*/


    public int StringToInt(String s){
        int temp = 0;
        for(int i=0;i<s.length();i++){
            temp = temp * 10;
            temp += charToInt(s.charAt(i));
        }
        return temp;
    }





    public static void prin(int[] a) {
        int i = 0;
        while (i < a.length) {
            System.out.print(a[i]);
            System.out.print(" ,");
            i++;
        }
        System.out.println("");
    }

    public String crossv(int fold, Instances dat) {
        Classifier cls = new Id3();
        String hasil = "cross validation failed";
        try {
            cls.buildClassifier(dat);
            Evaluation eval = new Evaluation(dat);
            eval.crossValidateModel(cls, dat, fold, new Random(1));
            hasil = (eval.toSummaryString("\nResults\n======\n", false));
        } catch (Exception ex) {
            Logger.getLogger(NaiveBayesianModel.class.getName()).log(Level.SEVERE, null, ex);
        }
        return hasil;
    }
    public String crossval(int fold,Instances dat) {
        DecisionTree asd = new DecisionTree();
        int i = 0;
        int j = 0;
        int k = 0;
        int bagi = (dat.numInstances() / fold);
        int[] hasil = new int[fold];


        String asss = "kosong.arff";
        Instances qwe = FileReader.readFile(asss);

        while (i < fold) {
            Instances tests = new Instances(qwe);
            tests.setClassIndex(0);
            int z = i * (dat.numInstances() / fold);
            while (z < ((i + 1) * bagi)) {
                Instance piece = dat.instance(z);
                tests.add(piece);
                z++;
            }
//            System.out.println(tests);

            DecisionTreeModel model = asd.generateModel(tests);
//            DecisionTreeModel model = new DecisionTreeModel(InstancesConverter.convert(tests));

            //mbuat model dati tests
            if (i == 0) {
                j = bagi;
                while (j < dat.numInstances()) {
                    Instance train = dat.instance(j);
                    String harus = train.toString();
                    if (!model.executeInstance(InstancesConverter.convert2(train)).equals(harus.substring(0, 1))) {
                        hasil[i] = hasil[i] + 1;
                    }
                    j++;
                }
            } else if (i == (fold - 1)) {
                j = 0;

                while (j < ((fold - 1) * bagi)) {
                    Instance train = dat.instance(j);
                    String harus = train.toString();
                    if (!model.executeInstance(InstancesConverter.convert2(train)).equals(harus.substring(0, 1))) {
                        hasil[i] = hasil[i] + 1;
                    }
                    j++;
                }

            } else {
                j = 0;
                while (j < (i * bagi)) {
                    Instance train = dat.instance(j);
                    String harus = train.toString();
                    if (!model.executeInstance(InstancesConverter.convert2(train)).equals(harus.substring(0, 1))) {
                        hasil[i] = hasil[i] + 1;
                    }
                    j++;
                }
                j = ((i + 1) * bagi);
                while (j < dat.numInstances()) {
                    Instance train = dat.instance(j);
                    String harus = train.toString();
                    if (!model.executeInstance(InstancesConverter.convert2(train)).equals(harus.substring(0, 1))) {
                        hasil[i] = hasil[i] + 1;
                    }
                    j++;
                }
            }

            j = 0;
            i++;
        }
//        System.out.println("Correctly classified instances   : " +rata(hasil)+ " instances    " +((double)rata(hasil)/(double)dat.numInstances()*100)+"%" );
//        System.out.println("Incorrectly classified instances : " +(dat.numInstances()-rata(hasil))+ " instances    " +((double)(dat.numInstances()-rata(hasil))/(double)dat.numInstances()*100)+"%" );
//        System.out.println("Root mean square error           : " +rms(accu(invers(hasil,dat.numInstances()),dat.numInstances())));
//        System.out.println("Number of Instances              : " +dat.numInstances() +" instances");
        String hasil1 = "Correctly classified instances   : " +rata(hasil)+ " instances    " +((double)rata(hasil)/(double)dat.numInstances()*100)+"%"+"\n";
        String hasil2 = "Incorrectly classified instances : " +(dat.numInstances()-rata(hasil))+ " instances    " +((double)(dat.numInstances()-rata(hasil))/(double)dat.numInstances()*100)+"%\n" ;
        String hasil3 = "Root mean square error           : " +rms(accu(invers(hasil,dat.numInstances()),dat.numInstances()))+"\n" ;
        String hasil4 = "Number of Instances              : " +dat.numInstances() +" instances\n";
        String shasil = hasil1+hasil2+hasil3+hasil4;
        return shasil;
    }

    public static int[] invers(int[] nums, int jum){
        for (int i = 0; i < nums.length; i++) {
            nums[i] = jum - nums[i];
        }
        return nums;
    }

     public static double rms(int[] nums) {
        double ms = 0;
        for (int i = 0; i < nums.length; i++) {
            ms += nums[i] * nums[i];
        }
        ms /= nums.length;
        return Math.sqrt(ms);
    }
    public static double rms(double[] nums){
        double ms = 0;
        for (int i = 0; i < nums.length; i++)
            ms += nums[i] * nums[i];
        ms /= nums.length;
        return Math.sqrt(ms);
    }
    public static int rata(int[] nums){
        int ms = 0;
        for (int i = 0; i < nums.length; i++) {
            ms = ms+ nums[i];
        }
        ms = ms / nums.length;
        return ms;
    }
    public static double[] accu(int[] nums , int jum){
        double[] acc = new double[nums.length];
        for (int i = 0; i < nums.length; i++) {
            acc[i] = ((double)nums[i]/(double)jum);
        }
        return acc;
    }








    public static void main(String args[]) throws IOException {
//        DecisionTree tes = new DecisionTree();
//        tes.isi("test.txt");
//        DecisionTreeModel model = tes.generateModel(tes.ar);
//        model.saveModel("tes.model");
        
                
//        BinarySearchTree tree = new BinarySearchTree(tes.executeQueue());
//        tree.Print(tree.root);

        String a = "C:\\AI\\specthearttrain.arff";
        FileReader file = new FileReader();
        int k = 5;
        Instances qwe = file.readFile(a);
        qwe.setClassIndex(0);
        DecisionTree asd = new DecisionTree();
        DecisionTreeModel zxc = asd.DecisionTreeWEKA(qwe);

        String hasil1 = zxc.crossv(5,qwe);
        String hasil = zxc.crossval(5, qwe);
        System.out.println(hasil1);
        System.out.println(hasil);
//        //System.out.println(rata(hasil));
//        double[] accu = accu(hasil,qwe.numInstances());
//        for(int q = 0; q < accu.length ; q++){
//            System.out.println(accu[q]);
//        }
//        System.out.println(rms(accu(hasil,qwe.numInstances())));


    }    
    
}
